package com.yixing.tech.wms.service.impl;

import com.yixing.tech.common.base.BusinessException;
import com.yixing.tech.common.enums.YesOrNo;
import com.yixing.tech.common.service.AbstractWmsService;
import com.yixing.tech.wms.entity.ManualStockWork;
import com.yixing.tech.wms.service.InitClearService;
import com.yixing.tech.wms.service.StorageAreaService;
import com.yixing.tech.wms.service.WmsTaskService;
import com.yixing.tech.wms.stock.out.StockOutConstants;
import com.yixing.tech.wms.stock.out.domain.Task;
import com.yixing.tech.wms.stock.out.domain.vo.TaskMethod;
import com.yixing.tech.wms.stock.out.domain.vo.TaskType;
import com.mongodb.client.result.UpdateResult;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.bson.Document;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.data.mongodb.core.aggregation.ConvertOperators;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.function.Supplier;
import java.util.stream.Collectors;

import static com.yixing.tech.common.CommonConstant.*;

/**
 * ContainerService
 *
 * @author yixing tech
 * @since 1.0.0
 */
@Slf4j
@Service
public class InitClearServiceImpl extends AbstractWmsService implements InitClearService {

    @Autowired
    private StorageAreaService storageAreaService;
    @Autowired
    private WmsTaskService wmsTaskService;

    private void assert0(Supplier<Boolean> supplier, String message) throws BusinessException {
        if (supplier.get()) {
            throw new BusinessException(message);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean initClear(String workCode) throws BusinessException {
        assert0(() -> StringUtils.isBlank(workCode), "作业号为空");
        Document initClear = simpleFindOne("entity_" + INIT_CLEAR_WORK, "workCode", workCode);
        assert0(() -> initClear == null, String.format("作业号[%s]不存在", workCode));
        String status = initClear.getString("status");
        assert0(() -> InitStatus.starting.name().equals(status) || InitStatus.running.equals(status),
                String.format("作业号[%s]已在执行中", workCode));

        // 必要字段
        String stockLocationCode = initClear.getString("stockLocationCode");
        assert0(() -> StringUtils.isBlank(stockLocationCode), "存储库位为空");
        String ab = initClear.getString("ab");
        assert0(() -> StringUtils.isBlank(ab), "ab库选择不能为空");

        // 校验改库位是否存在未完成的作业
        List<Document> unfinished = mongoTemplate.find(Query.query(newCriteria()
                        .and("stockLocationCode").is(stockLocationCode)
                        .and("workCode").ne(workCode)
                        .and("status").ne(InitStatus.finished.name())),
                Document.class, "entity_" + INIT_CLEAR_WORK);
        assert0(() -> !CollectionUtils.isEmpty(unfinished), String.format("存在未执行完成的初始化清除作业: %s",
                unfinished.stream().map(x -> x.getString("workCode")).collect(Collectors.joining(","))));

        String pickAreaCode;
        Document stockArea;
        if (A.equals(ab)) {
            // A库分拣区，固定
            pickAreaCode = storageAreaService.validateGetFirstAreaCode(AreaType.stereoscopicSorting);
            stockArea = simpleFindOne("entity_" + STOCK_AREA, "code", "100104");
        } else if (B.equals(ab)) {
            // B库分拣区，固定
            pickAreaCode = AreaCodeDict.bMatPickingEntrance.getText();
            stockArea = simpleFindOne("entity_" + STOCK_AREA, "code", "100203");
        } else {
            stockArea = null;
            pickAreaCode = null;
        }
        assert0(() -> pickAreaCode == null, String.format("根据ab库[%s]未找到对应的分拣库区", ab));
        assert0(() -> stockArea == null, String.format("根据ab库[%s]未找到对应的存储库区", ab));

        Document stockLocation = simpleFindOne("entity_" + STOCK_LOCATION, "code", stockLocationCode);
        assert0(() -> stockLocation == null, String.format("库位[%s]不存在", stockLocationCode));
        assert0(() -> stockLocation.getBoolean("lock"), String.format("库位[%s]被锁定", stockLocationCode));
        assert0(() -> !stockArea.getString("code").equals(stockLocation.getString("area")), String.format("库位[%s]不在[%s]库中", stockLocationCode, ab));
        String containerCode = stockLocation.getString("containerCode");
        assert0(() -> StringUtils.isBlank(containerCode), String.format("库位[%s]上未绑定容器", stockLocationCode));
        Document container = simpleFindOne("entity_" + CONTAINER, "code", containerCode);
        assert0(() -> container == null, String.format("容器[%s]不存在", containerCode));
        assert0(() -> container.getBoolean("lock"), String.format("容器[%s]被锁定", containerCode));

        // 锁库位
        Update u = new Update();
        u.set("lock", true);
        u.set("updateTime", new Date());
        boolean suc = versionUpdateById(stockLocation, u, "entity_" + STOCK_LOCATION);
        assert0(() -> !suc, "锁库位失败，重新尝试");

        // 创建下架任务
        Task task = Task.builder()
                .taskCode(UUID.randomUUID().toString().replaceAll("-", ""))
                .taskType(TaskType.STOCK_OUT.getId())
                .startLoc(stockLocationCode)
                .endArea(pickAreaCode)
                .podCode(containerCode)
                .business(INIT_CLEAR_WORK_BIZ)
                .operationNo(Collections.singletonList(workCode))
                .build();
        wmsTaskService.saveTaskInfo(task);

        Update u1 = new Update();
        u1.set("status", InitStatus.starting.name());
        u1.set("updateTime", new Date());
        // 辅助信息
        u1.set("containerCode", containerCode);
        updateById(initClear, u1, "entity_" + INIT_CLEAR_WORK);
        return true;
    }

    @Override
    public void handleCallback(String taskCode, String method, String locCode, String podCode) {
        log.info("清除下架 处理任务回调信息:taskCode[{}],method[{}],locCode[{}],podCode[{}]", taskCode, method, locCode, podCode);

        List<AggregationOperation> operations = new ArrayList<>();
        operations.add(Aggregation.match(Criteria.where("tenantId").is(tenantId).and("isDelete").ne(YesOrNo.YES.ordinal())
                .and("taskCode").is(taskCode)));
        operations.add(Aggregation.addFields()
                .addFieldWithValue("taskType", ConvertOperators.ToString.toString("$taskType.id"))
                .addFieldWithValue("status", ConvertOperators.ToString.toString("$status.id"))
                .addFieldWithValue("performStatus", ConvertOperators.ToString.toString("$performStatus.id"))
                .build());

        AggregationResults<Task> results = mongoTemplate.aggregate(Aggregation.newAggregation(operations), StockOutConstants.TASK_TEMPLATE, Task.class);
        if (ObjectUtils.isEmpty(results.getMappedResults())) {
            return;
        }
        Task task = results.getMappedResults().get(0);
        if (TaskMethod.OUTBIN.getAction().equals(method)) {
            // 处理离开起点
            handleOutBin(taskCode, method, locCode, podCode, task);
        } else if (TaskType.STOCK_OUT.getId().equals(task.getTaskType()) && TaskMethod.FINISH.getAction().equals(method)) {
            // 容器到达分拣区
            containerArrive(taskCode, method, locCode, podCode, task);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void containerArrive(String taskCode, String method, String locCode, String podCode, Task task) {
        List<String> operationNo = task.getOperationNo();
        String abc = null;
        if (!CollectionUtils.isEmpty(operationNo)) {
            Update update = new Update();
            update.set("status", ManualStockWork.ManualStockWorkStatus.finished.name());
            update.set("endLocation", locCode);
            update.set("updateTime", new Date());
            mongoTemplate.updateMulti((Query.query(newCriteria().and("workCode").in(operationNo))), update, "entity_" + INIT_CLEAR_WORK);

            // 下架到达分拣台入口的时候，不需要绑定库位与料箱
            List<ManualStockWork> inStockTasks = mongoTemplate.find(Query.query(newCriteria().and("workCode").in(task.getOperationNo())),
                    ManualStockWork.class, "entity_" + INIT_CLEAR_WORK);
            if (!CollectionUtils.isEmpty(inStockTasks)) {
                abc = inStockTasks.get(0).getAbc();
            }
        }

        // 查询库位信息
        Document locDoc = simpleFindOne("entity_" + STOCK_LOCATION, "code", locCode);
        if (locDoc != null) {
            // 解锁容器
            unlockContainer(podCode);
            // 容器到达分拣区
            Update update = new Update();
            update.set("lock", false);
            if (!Objects.equals(locDoc.getString("area"), AreaCodeDict.bMatPickingEntrance.getText())) {
                update.set("containerCode", podCode);
            }
            UpdateResult updateResult = mongoTemplate.updateFirst(Query.query(newCriteria().and("code").is(locCode)),
                    update, "entity_" + STOCK_LOCATION);
            log.info("清除下架 任务离开起点到达分拣区,更新库位[{}]绑定的容器[{}],更新结果[{}]", locCode, update, updateResult);
        } else {
            log.warn("清除下架 分拣台库位不存在，编码是: ", locCode);
        }

        // 清除库存
        Update delete = new Update();
        delete.set("isDelete", 1);
        UpdateResult updateResult1 = mongoTemplate.updateMulti(Query.query(newCriteria().and("containerCode").is(podCode)),
                delete, "entity_" + STOCK_NAME);
        log.info("清除下架 删除容器是[{}]的库存，更新条件: [{}], 更新结果: [{}]", podCode, delete, updateResult1);
    }

    private void unlockContainer(String containerCode) {
        Update update2 = new Update();
        update2.set("lock", false);
        update2.set("updateTime", new Date());
        mongoTemplate.updateFirst((Query.query(newCriteria().and("code").is(containerCode))), update2, "entity_" + CONTAINER);
    }

    @Transactional(rollbackFor = Exception.class)
    public void handleOutBin(String taskCode, String method, String locCode, String podCode, Task task) {
        List<String> operationNo = task.getOperationNo();
        if (!CollectionUtils.isEmpty(operationNo)) {
            Update update = new Update();
            update.set("status", InitStatus.starting.name());
            update.set("updateTime", new Date());
            mongoTemplate.updateMulti((Query.query(newCriteria().and("code").in(operationNo))), update, "entity_" + INIT_IN_WORK);
        }

        // 处理离开起点
        // 起点库位解绑容器，并解锁
        Update update = new Update();
        update.unset("containerCode");
        update.set("lock", false);
        UpdateResult updateResult = mongoTemplate.updateFirst(Query.query(newCriteria().and("code").is(locCode)), update,
                "entity_" + STOCK_LOCATION);
        log.info("任务离开起点节点回调,更新库位[{}]绑定的容器[{}],更新结果[{}]", locCode, update, updateResult);
    }


}
